/*
* Copyright (c) 2011 Connection Road.  All rights reserved.
* This program and the accompanying material are made available
* under the terms of the Eclipse Public Licnese v1.0 which is
* available at http://www.eclipse.org/legal/epl-v10.html
*/

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Deployment.Application;
using CrSyncFilesForms.Utils;
using Microsoft.Win32;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
using CrSyncFilesForms.Properties;
using CrSyncFilesUtils;

namespace CrSyncFilesForms
{
    /// <summary>
    /// Class for holding various static and global variables
    /// </summary>
    public static partial class ApplicationGlobals
    {
        public static FileSyncPairManager PairManager { get; set; }
        public static CrSyncToyManager SyncToyManager { get; set; }
        public static RegistryManager RegistryManager { get; set; }
        public static int CfgDefaultPauseTime;

        public static string CrSyncConfigDirectory;
        public static string CrSyncStoreFilePath;
        public static string CrSyncLogFilePath;
        public static string CrSyncCriticalLogFilePath;
        public static string MetaDataDirectory;
        public static string UniqueIdentifier = "702e009670110f389e30524f4abe6697";
        public static string EventSource = "CRSyncFiles";
        public static string RegistryKey;
        public static string Build;
        public static string ReleaseName;
        public static string ReleaseVersion;
        public static string CodeName;
        public static string LongVersion;
        public static string Prefix;

        public static bool Verbose = true;
        public static bool EnableCloud; // Lots of UI elements depend on this variable.
        public static bool LockVerbose;
        public static string VerboseMessages;
        public static int VerboseBufferSize ; // Verbose message character limit

        static partial void InitializeExtension();
        
        
        public static void InitializeApplicationConfiguration()
        {
            DoRunStateCheck();
           // Add your default application environment variable below
            CfgDefaultPauseTime = 0;
            VerboseBufferSize = Settings.Default.CFG_VERBOSE_MAX_BUFFER_SIZE;
            VerboseMessages = "";
            Verbose = false;

            if (!string.IsNullOrEmpty(ConfigurationManager.AppSettings["AppDataPostFix"]))
            {
                CrSyncConfigDirectory = new FileNode(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)).Join(Application.CompanyName).Join(Application.ProductName).Join(ConfigurationManager.AppSettings["AppDataPostFix"]).Path;
            }
            else
            {
                CrSyncConfigDirectory = new FileNode(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)).Join(Application.CompanyName).Join(Application.ProductName).Path;
            }
            
            CrSyncStoreFilePath = new FileNode(CrSyncConfigDirectory).Join("CrSyncFiles.xml").Path;
            CrSyncLogFilePath = new FileNode(CrSyncConfigDirectory).Join("CrSyncFilesExecution.log").Path;
            CrSyncCriticalLogFilePath = new FileNode(CrSyncConfigDirectory).Join("CrSyncFilesCriticalErrors.log").Path;
            
            
            RegistryKey = @"Software\" + Application.CompanyName + @"\" + Application.ProductName;
            RegistryManager = new RegistryManager(RegistryKey);


            if (!string.IsNullOrEmpty(ConfigurationManager.AppSettings["AppDataPostFix"]))
            {
                // Assume as the master app and do nothing with SyncToy integration.

                MetaDataDirectory = new FileNode(CrSyncConfigDirectory).Join("Metafiles").Path;
                Prefix = "CrSyncFiles_";
            }
            else
            {
                if (Settings.Default.CFG_ENABLE_SYNCTOY_INTEGRATION)
                {
                    MetaDataDirectory = new FileNode(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)).Join("Microsoft").Join("SyncToy").Join("2.0").Path;
                    Prefix = "SyncToy_";
                }
                else
                {
                    MetaDataDirectory = new FileNode(CrSyncConfigDirectory).Join("Metafiles").Path;
                    Prefix = "CrSyncFiles_";
                }
            }

            if (!Directory.Exists(MetaDataDirectory))
            {
                Directory.CreateDirectory(MetaDataDirectory);
            }

            PairManager = new FileSyncPairManager(CrSyncStoreFilePath);

            if (!string.IsNullOrEmpty(ConfigurationManager.AppSettings["AppDataPostFix"]))
            {
                // Disable SyncToy Integration
                SyncToyManager = new CrSyncToyManager(false);
            }
            else
            {
                SyncToyManager = new CrSyncToyManager(Settings.Default.CFG_ENABLE_SYNCTOY_INTEGRATION);
            }
            
            Build = Application.ProductVersion.Split('.')[2] + Application.ProductVersion.Split('.')[3];
            ReleaseName = "RC1";
            ReleaseVersion = Application.ProductVersion.Split('.')[0] + "." + Application.ProductVersion.Split('.')[1] + "." + Build;
            CodeName = "Synchronysaurus Rex";
            LongVersion = ReleaseVersion + " " + CodeName + " RC1";


            if((string.IsNullOrEmpty(Settings.Default.CFG_UNIQUE_IDENTIFIER)) || (Settings.Default.CFG_UNIQUE_IDENTIFIER == "null") || (Settings.Default.CFG_UNIQUE_IDENTIFIER != UniqueIdentifier) )
            {
                Settings.Default.CFG_UNIQUE_IDENTIFIER = UniqueIdentifier;
                Settings.Default.Save();
            }

            
            //---
            InitializeExtension();


            // Clean the meta folder
            //CleanMetaDataDirectory(PairManager);
            CheckLastFolderPair();
            
            // Registry
            InitializeRegistry();

            // Update metada directory when CRSyncFiles is the master
            if ((Settings.Default.CFG_RUN_STATE == (int)CrSyncFilesRunState.Master) || (!string.IsNullOrEmpty(ConfigurationManager.AppSettings["AppDataPostFix"])))
            {
                PairManager.UpdateMetaDataDiretory(MetaDataDirectory);
            }

            LoadVerboseWelcomeMessage();
        }

        public static void UpdateApplicationConfiguration()
        {
            if (!string.IsNullOrEmpty(ConfigurationManager.AppSettings["AppDataPostFix"]))
            {
                SyncToyManager.EnableSyncToyIntegration = false;
            }
            else
            {
                SyncToyManager.EnableSyncToyIntegration = Settings.Default.CFG_ENABLE_SYNCTOY_INTEGRATION;
            }
        }

        /// <summary>
        /// Verifies if SyncToy is/was installed.
        /// </summary>
        public static void DoRunStateCheck()
        {
            if (!string.IsNullOrEmpty(ConfigurationManager.AppSettings["AppDataPostFix"]))
            {
                // Do not check for run state. Automatically assume as the master app.

                //Settings.Default.CFG_ENABLE_SYNCTOY_INTEGRATION = false;
                //Settings.Default.CFG_RUN_STATE = (int)CrSyncFilesRunState.Master;
                //Settings.Default.Save();
            }
            else
            {
                switch (Settings.Default.CFG_RUN_STATE)
                {
                    case (int)CrSyncFilesRunState.NotRunYet:
                        if (CrSyncFilesUtility.IsSyncToyInstalled())
                        {
                            Settings.Default.CFG_ENABLE_SYNCTOY_INTEGRATION = true;
                            Settings.Default.CFG_RUN_STATE = (int)CrSyncFilesRunState.Slave;
                            Settings.Default.Save();
                        }
                        else
                        {
                            Settings.Default.CFG_ENABLE_SYNCTOY_INTEGRATION = false;
                            Settings.Default.CFG_RUN_STATE = (int)CrSyncFilesRunState.Master;
                            Settings.Default.Save();
                        }
                        break;
                    case (int)CrSyncFilesRunState.Slave:
                        if (!CrSyncFilesUtility.IsSyncToyInstalled())
                        {
                            Settings.Default.CFG_ENABLE_SYNCTOY_INTEGRATION = false;
                            Settings.Default.CFG_RUN_STATE = (int)CrSyncFilesRunState.Master;
                            Settings.Default.Save();
                        }
                        break;
                    case (int)CrSyncFilesRunState.Master:
                        //Do nothing
                        break;
                    default:
                        Settings.Default.CFG_ENABLE_SYNCTOY_INTEGRATION = true;
                        Settings.Default.CFG_RUN_STATE = (int)CrSyncFilesRunState.Slave;
                        Settings.Default.Save();
                        break;

                }
            }
        }

        /// <summary>
        /// Performs a two-way import and master-slave verification.
        /// Ensures that only do a two-way imports if CrSyncFiles is a slave else ignore SyncToy's store file.
        /// </summary>
        public static void SilentlyImportSynctoyFolderPairs()
        {

            if (!string.IsNullOrEmpty(ConfigurationManager.AppSettings["AppDataPostFix"]))
            {
                //Settings.Default.CFG_ENABLE_SYNCTOY_INTEGRATION = false;
                //Settings.Default.CFG_RUN_STATE = (int)CrSyncFilesRunState.Master;
                //Settings.Default.Save();
            }
            else if (Settings.Default.CFG_ENABLE_SYNCTOY_INTEGRATION)
            {
                var runState = (CrSyncFilesRunState)Settings.Default.CFG_RUN_STATE;
                if ((runState == CrSyncFilesRunState.NotRunYet) || (runState == CrSyncFilesRunState.Slave))
                {
                    CrSyncFilesUtility.CheckForUnavailableSyncToyFolderPairs(MetaDataDirectory, PairManager, SyncToyManager);

                    CrSyncFilesUtility.ImportAvailableSyncToyFolderPairs(Prefix, MetaDataDirectory, PairManager, SyncToyManager);

                    CrSyncFilesUtility.ImportAvailableCrSyncFiles(MetaDataDirectory, PairManager, SyncToyManager);
                }
            }
        }


        /// <summary>
        /// Write to windows event viewer.
        /// </summary>
        /// <param name="message"></param>
        /// <param name="entryType"></param>

        public static void WriteEventLog(string message,EventLogEntryType entryType)
        {
           try
           {
               if (Settings.Default.CFG_WRITE_TO_EVENT_VIEWER)
               {
                   if (!EventLog.SourceExists(EventSource))
                   {
                       EventLog.CreateEventSource(EventSource, "Application");
                   }

                   //Logs
                   EventLog.WriteEntry(EventSource, message, entryType);
               }
           }
           catch (Exception exception)
           {
               SetVerboseMessage(exception.Message);
               SetVerboseMessage(exception.ToString());
           }
        }


        public static void CheckLastFolderPair()
        {
            if ((Settings.Default.CFG_RUN_STATE == (int)CrSyncFilesRunState.Slave) || (Settings.Default.CFG_RUN_STATE == (int)CrSyncFilesRunState.NotRunYet))
            {
                Settings.Default.CFG_LAST_FOLDER_PAIR = CrSyncFilesUtility.GetSyncToyLastFolderPair();
                Settings.Default.Save();
            }
        }


        public static void SetLastRunFolderPair(string folderPairName)
        {
            Settings.Default.CFG_LAST_FOLDER_PAIR = folderPairName;
            Settings.Default.Save();
            if ((Settings.Default.CFG_RUN_STATE == (int)CrSyncFilesRunState.Slave) || (Settings.Default.CFG_RUN_STATE == (int)CrSyncFilesRunState.NotRunYet))
            {
                CrSyncFilesUtility.SetSyncToyLastFolderPair(folderPairName);
            }
        }

        public static void InitializeRegistry()
        {
            var applicationName = Application.ProductName;
            var applicationVersion = Application.ProductVersion;
            var companyName = Application.CompanyName;
            

            var registry = Registry.CurrentUser.CreateSubKey(RegistryKey);
            if (registry != null)
            {
                var runState = "Not run yet";
                registry.SetValue("APPLICATION_NAME", applicationName);
                registry.SetValue("COMPANY_NAME", companyName);
                registry.SetValue("APPLICATION_VERSION", applicationVersion);
                registry.SetValue("RELEASE_VERSION", ReleaseVersion);
                registry.SetValue("RELEASE_NAME", ReleaseName);
                registry.SetValue("CODE_NAME", CodeName);
                registry.SetValue("LONG_VERSION", LongVersion);
                registry.SetValue("METADATA_DIRECTORY", MetaDataDirectory);
                registry.SetValue("STORE_FILE", CrSyncStoreFilePath);
                if (string.IsNullOrEmpty(ConfigurationManager.AppSettings["AppDataPostFix"]))
                {
                    registry.SetValue("MODE","RELEASE");
                }
                else
                {
                    registry.SetValue("MODE", "DEVELOPMENT");
                }

                if (ApplicationDeployment.IsNetworkDeployed)
                {
                    registry.SetValue("DEPLOYMENT", "CLICKONCE");
                }
                else
                {
                    registry.SetValue("DEPLOYMENT", "STANDARD");
                }

                switch (Settings.Default.CFG_RUN_STATE)
                {
                    case (int)CrSyncFilesRunState.NotRunYet:
                        runState = CrSyncFilesRunState.NotRunYet.ToString();
                        break;
                    case (int)CrSyncFilesRunState.Master:
                        runState = CrSyncFilesRunState.Master.ToString();
                        break;
                    case (int)CrSyncFilesRunState.Slave:
                        runState = CrSyncFilesRunState.Slave.ToString();
                        break;
                    default :
                        runState = CrSyncFilesRunState.Slave.ToString();
                        break;
                }

                registry.SetValue("RUN_STATE", runState);
            }

        }


        /// <summary>
        /// Imports previous version of CRSyncFiles' folder pairs.
        /// Only call this method if CRSyncFiles' current run state is in master mode.
        /// And must be called prior to Regstry entries.
        /// </summary>
        public  static void ImportPreviousVersion()
        {
            try
            {
                var crsynfilesRegistry = Registry.CurrentUser.OpenSubKey(RegistryKey);
                if(crsynfilesRegistry != null)
                {
                    var registeredVersion = (string) crsynfilesRegistry.GetValue("RELEASE_VERSION", null);
                    if(registeredVersion.CompareTo(ReleaseVersion) < 0)
                    {
                        var oldStoreFilePath = (string) crsynfilesRegistry.GetValue("STORE_FILE", null);
                        if(!string.IsNullOrEmpty(oldStoreFilePath))
                        {
                            var oldFileManager = new FileSyncPairManager(oldStoreFilePath);
                            var availableFolderPairs = oldFileManager.AvailableFolderPairs();
                            
                            foreach(var folderPair in availableFolderPairs)
                            {
                                PairManager.AddFolderPair(folderPair.Value);
                            }
                        }
                    }
                }
            }
            catch
            {
                //TODO: IGNORE
            }
        }


        /// <summary>
        /// Appends a string message to the verbose message buffer.
        /// </summary>
        /// <param name="message"></param>
        public static void SetVerboseMessage(string message)
        {
            var dateTimeString = string.Format("[{0:MM/dd/yyyy hh:mm:ss tt}]  ", DateTime.Now);
            if(Verbose)
            {

                // Truncate verbose message if it reaches the specified character limit
                if(message != " ")
                {
                    if(VerboseBufferSize != 0)
                    {
                        if (VerboseMessages.Length >= VerboseBufferSize)
                        {
                            TruncateVerboseMessage(message.Length);
                        }
                    }
                }

                if ( (!string.IsNullOrEmpty(VerboseMessages)) && (VerboseMessages.Length >= 1) )
                {
                    if (message.Trim().Length >= 1)
                    {
                        VerboseMessages += "\r\n" + dateTimeString + message;
                    }
                    else
                    {
                        VerboseMessages += "\r\n" + message;
                    }
                }
                else
                {
                    if (message.Trim().Length >= 1)
                    {
                        VerboseMessages += dateTimeString + message;
                    }
                    else
                    {
                        VerboseMessages += "\r\n" + message;
                    }
                }
            }
        }

        /// <summary>
        /// Truncates the verbose message starting from first charater up to
        /// the specified length.
        /// </summary>
        /// <param name="length">Amount to truncate</param>
        public static  void TruncateVerboseMessage(int length)
        {
            int startPosition;
            int endposition;

            if(length >= VerboseBufferSize)
            {
                startPosition = 0;
                endposition = VerboseMessages.Length;
            }
            else
            {
                startPosition = 0;
                endposition = length;
            }

            var truncatedVerboseMessage = VerboseMessages.Substring(startPosition, endposition);
            VerboseMessages = truncatedVerboseMessage;
        }

        public static Dictionary<string,object> GetUserSettings()
        {
            var pkv = new Dictionary<string, object>();
            foreach (SettingsProperty property in Settings.Default.Properties)
            {
                if ((property.Name == "CFG_CLOUD_USER_PASSWORD") || (property.Name == "CFG_CLOUD_USER_PASSWORD_2"))
                {
                    pkv.Add(property.Name, "*****");
                }
                else
                {
                    var settingsBase = (SettingsBase)Settings.Default;
                    pkv.Add(property.Name, settingsBase[property.Name]);
                }
            }

            return pkv;
        }


        public static void LoadSystemInfo()
        {
            VerboseMessages += string.Format("\r\n\r\nPlatform {0}", Environment.OSVersion.Platform);
            VerboseMessages += string.Format("\r\nVersion {0}", Environment.OSVersion.Version);
            VerboseMessages += string.Format("\r\n{0}bit\r\n\r\n\r\n\r\n\r\n", (IntPtr.Size >= 8) ? "64" : "32");
        }

        public static void LoadSettingsInformation()
        {
            VerboseMessages += "\r\n+--- USER SETTINGS -----------------------------------------------+\r\n";
            foreach (var pkv in GetUserSettings())
            {
                if (pkv.Key == "CFG_CLOUD_PASSWORD")
                {
                    VerboseMessages += string.Format("\r\n|   {0} = {1}", pkv.Key, "*****");
                }
                else
                {
                    VerboseMessages += string.Format("\r\n|   {0} = {1}", pkv.Key, pkv.Value);
                }
            }
            VerboseMessages += "\r\n+-----------------------------------------------------------+\r\n";
        }

        public static void LoadVerboseWelcomeMessage()
        {
            VerboseMessages = string.Format("+-------------------------------------------------------------------+\r\n"
                                            + "| Connection Road Sync Files {0}                                   |\r\n"
                                            + "+-------------------------------------------------------------------+\r\n",
                                            ReleaseVersion);
            LoadSettingsInformation();
            LoadSystemInfo();
        }

        public static bool WriteUserDefaults(string key, string type, string value)
        {
            foreach (SettingsProperty propertyKv in Settings.Default.Properties)
            {
                if (propertyKv.Name == key)
                {


                    if(type == "auto")
                    {

                        switch (propertyKv.PropertyType.Name)
                        {
                            case "String":
                                type = "string";
                                break;
                            case "Int64":
                            case "Int32":
                                type = "int";
                                break;
                            case "Boolean":
                                type = "bool";
                                break;
                            case "Double":
                                type = "double";
                                break;
                            case "Long":
                                type = "long";
                                break;
                            default:
                                type = propertyKv.PropertyType.Name;
                                break;
                        }

                    }

                    switch (type)
                    {
                        case "string":
                            Settings.Default[key] = value;
                            Settings.Default.Save();
                            return true;
                        case "int":
                            Settings.Default[key] = int.Parse(value);
                            Settings.Default.Save();
                            return true;
                        case "bool":
                            Settings.Default[key] = bool.Parse(value);
                            Settings.Default.Save();
                            return true;
                        case "double":
                            Settings.Default[key] = double.Parse(value);
                            Settings.Default.Save();
                            return true;
                        case "long":
                            Settings.Default[key] = long.Parse(value);
                            Settings.Default.Save();
                            return true;
                        default:
                            Console.WriteLine("The type named " + type + " is not yet registered...");
                            return false;
                    }
                }
            }

            return true;
        }
        
    }
}
